﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//Muu 13jan

namespace command1
{
    class Program
    {
        //Globala variabler
        static bool gameInProgress = true;
        static int emptyPiles = 0;
        static int round = 1;

        static int numTypes = 40;
        static int numPlayers = 2;
        static cardType[] allCardTypes = new cardType[numTypes];	//innehåller alla olika sorters kort och hur många de är
        static player[] allPlayers = new player[numPlayers];	//innehåller alla spelare

        //MAIN
        static void Main(string[] args)
        {
            //INIT
            for (int i = 0; i < numTypes; i++)
            {
                allCardTypes[i] = new cardType();
            }

            for (int i = 0; i < numPlayers; i++)
            {
                allPlayers[i] = new player();
            }

            initCardTypes();	//loads all types of cards
			for (int i=0;i<numPlayers;i++)	    //initiates every player for the game
			{
				allPlayers[i].init();		//dealing them cards
				allPlayers[i].drawCards(5);	    //and draws a hand (shuffles inside method)
                allPlayers[i].reset(); 			//reset: actions=1, money=0, nbuy=1;
			}

			//GAME
            while (gameInProgress)  //loop för runda
            {
				Console.Write("\n\nIt is player {0}'s turn", allPlayers[round%numPlayers].name);
				
				//use of actions
                while(allPlayers[round%numPlayers].actions>0)
				{
					allPlayers[round%numPlayers].refreshMoneyTreasure();
					allPlayers[round%numPlayers].displayStat();
					allPlayers[round%numPlayers].displayHand();
					
					if(allPlayers[round%numPlayers].readUseCard())	//reads which card to use and uses
					{												//if dont want to use action: return true
                        Console.Write("\n you don't want to use an action I see");
						allPlayers[round%numPlayers].actions=0;	//wants to continue without using an action or has no actions in hand
					}
					allPlayers[round%numPlayers].actions --;	//decreases by one for each action used
				}

                allPlayers[round % numPlayers].displayHand();

				//fixes money issues
				allPlayers[round%numPlayers].refreshMoneyTreasure();
				allPlayers[round%numPlayers].money += allPlayers[round%numPlayers].moneyTreasure;
				allPlayers[round%numPlayers].moneyTreasure=0;
				
				//buy
				Console.Write("\n\nShopping time!");
				while(allPlayers[round%numPlayers].nbuy>0)
				{
					allPlayers[round%numPlayers].displayStat();
					
					if(allPlayers[round%numPlayers].buyCard())
					{
						allPlayers[round%numPlayers].nbuy--;	//If there was a successful buy: decrease nbuy
					};
				}
				
                //end of turn
                allPlayers[round % numPlayers].discardHand();	//also erases values in it (so it doesn count with refrMoneyT)
				allPlayers[round%numPlayers].reset(); 			//reset: actions=1, money=0, nbuy=1;
                allPlayers[round % numPlayers].drawCards(5);
				
				if(emptyPiles>2) gameInProgress=false;
                round++;
            }
			
			//count victory points
            for (int i = 0; i < numPlayers; i++)
            {
                allPlayers[i].discardHand();
                allPlayers[i].discardDeck();
                //alla kort ligger nu i discardPile
                allPlayers[i].countPoints();
            }
            //Compare points between players
        }
        static void attack(int nType)
        {
            switch (nType)
            {
                case 19:    //Witch
                    for (int i = 1; i < numPlayers; i++)    //Från 1 istället fr 0 => den som attackerade förlorar inget
                    {
                        allPlayers[(i + round) % numPlayers].gainCard(39);  //Gains a curse card!!!
                    }
                    break;
                case 26:    //Council Room
                    for (int i = 1; i < numPlayers; i++)    //Från 1 istället fr 0 => den som attackerade förlorar inget
                    {
                        allPlayers[(i + round) % numPlayers].drawCards(1);
                    }
                    break;

                case 28:    //Militia
                    bool isACard = false;
                    for (int i = 1; i < numPlayers; i++)    //Från 1 istället fr 0 => den som attackerade förlorar inget
                    {
                        if (allPlayers[(i + round) % numPlayers].hasDefenceInHand())
                        {
                            Console.Write("\n\nPlayer {0} is safe from this attack",allPlayers[(i + round) % numPlayers].name);
                            continue;
                        }

                        Console.Write("\n\nIt is time for player {0} to discard cards\n", allPlayers[ (i + round) % numPlayers].name);
                        allPlayers[(i + round) % numPlayers].displayHand();
                        while (allPlayers[(i + round) % numPlayers].handCounter > 2)   //Lämnar 3 kort kvar på hand
                        {
                            Console.Write("\nChoose a card to discard from your hand: (number in hand)");
                            while (isACard == false)
                            {
                                isACard=allPlayers[(i + round) % numPlayers].discardCardInHand(Convert.ToInt32( Console.ReadLine()));
                            }
                        }
                    }
                    break;

                default:
                    break;
            }
        }

		class game	//Spar klass
		{
			int G_numTypes;
			int G_numPlayers;
			int G_round;
            int G_emptypiles;
			cardType[] G_allCardTypes;
			string[] G_typeNameIndex;
			player[] G_allPlayers;
		}
		
        class cardType
        {
            public string type; //victory, treasure, action.. använder väldigt sällan denna, om ens någonsin, det är smidigare att använda switch eller större mindre än
            //public int index;		//seems unnecessary
            public string name;
			public int cost;
			public int amount;	//how many cards are there availible in this game of this type
			//public bool[] btype;	//very good for hybrids: cards that are both victories and actions etc..

            public bool assignCard(string Ctype, int Cindex, string Cname, int Ccost, int Camount)
            {
                type = Ctype;
                //index = Cindex;
                name = Cname;
				cost = Ccost;
				amount = Camount;
                return true;
            }
			public bool assignCard(string Ctype, string Cname, int Ccost, int Camount)	//overloadded assign without "index"
            {
                type = Ctype;
                //index = Cindex;
                name = Cname;
				cost = Ccost;
				amount = Camount;
                return true;
            }
			public void printWhatCardIAm()
			{
				Console.Write("\n{0}",name);
			}
        }
        class player
        {
            public string name;
            //public int playerNumber;
			
			public int money;			//from Actions
			public int moneyTreasure;	//from Treasure cards
            public int actions;
			public int nbuy;

            public int points;
			
			//Card handlers
            public int numOfCardsInTotal;
            public int[] cardsInHand;
            public int[] deck;
			public int[] discardPile;
			public int[] usedCards;
			public int handCounter;
			public int deckCounter;
			public int discardCounter;
			public int usedCounter;

            public player()	//constructor
            {
                cardsInHand = 	new int[50];
				usedCards = 	new int[50];
                deck = 			new int[200];
				discardPile = 	new int[200];
                
				handCounter = -1;	//Counters are actually pointers inside resp. array
				deckCounter = -1;	//deckCounter = 0 innebär det finns ett kort, dvs "kort 0", Counter=-1 innebär inget kort finns
				discardCounter = -1;
				usedCounter = -1;
                points = 0;
            }
			public bool init ()	//lägga in i constructor?
			{
				numOfCardsInTotal = 0;	//increases inside gainCard(int)

                Console.Write("\nWhat is your name? ");
                this.name = Console.ReadLine();
				
                //Sets all card arrays to blanks
				for (int i=0;i<this.cardsInHand.Length;i++)
				{
					this.cardsInHand[i]=-2;	//that is empty card space
				}
                for (int i = 0; i < this.deck.Length; i++)
                {
                    this.deck[i] = -3;	//that is empty card space
                }
                for (int i = 0; i < this.discardPile.Length; i++)
                {
                    this.discardPile[i] = -4;	//that is empty card space
                }
                for (int i = 0; i < this.usedCards.Length; i++)
                {
                    this.usedCards[i] = -5;	//that is empty card space
                }

                //Gain cards
                for (int i = 0; i < 3; i++)	//Gain 3 Estates
                {
                    this.gainCard(3);
                }
                for (int i = 0; i < 7; i++)	//Gain 7 Copper
                {
                    this.gainCard(0);
                }
				return true;
			}
            public bool hasDefenceInHand()
            {
                if (findCardInHand(31) > -1) return true;  //31 == MOAT, ett försvarskort
                return false;
            }
            public int findCardInHand(int nType)
            {
                for (int i = 0; i < handCounter+1; i++)
                {
                    if (cardsInHand[i] == nType) return i;
                }
                return -1;
            }
            public bool reset()	//Resets player for every round
            {
                this.money = 0;
				this.moneyTreasure=0;
                this.actions = 1;
				this.nbuy = 1;
                
                return true;
            }
            public bool drawCards(int amountOfCards)
            {
				for (int i=0; i<amountOfCards; i++)
				{
					if (this.deckCounter<0)	//det finns inget kort => shuffle!
					{
						if(this.discardCounter<0)	//båda högarna är tomma, dvs alla kort finns redan på bordet
						{
							Console.Write("\nCongratulations, your round was so successful that you have already used all your cards. Both the deck and the discard pile is empty");
							return false;
						}
						shuffle.shuffleIntArr(ref this.discardPile, ref this.discardCounter, ref this.deck, ref this.deckCounter);
					}
					
					this.cardsInHand[++this.handCounter] = this.deck[this.deckCounter];	//copies value (card) to new place in Hand
					this.deck[this.deckCounter] = -3;	//could erase value in deck but that is actually unnessecary, i will delete this line when everything works
					this.deckCounter--;	//one less card in deck
				}
                return true;
            }
            public bool discardCardInHand(int number)
            {
                try
                {
                    if (this.cardsInHand[number] == -2)
                    {
                        Console.Write("That is not a card");
                        return false;
                    }
                }
                catch (NullReferenceException)
                {
                    Console.Write("NullRef: That is not a card");
                    return false;
                }
                this.discardPile[discardCounter] = this.cardsInHand[number];
                this.cardsInHand[number] = -2;
                this.discardCounter++;
                this.handCounter--;
                return true;
            }
            public bool discardHand()
            {
                for (int i = 0; i < this.cardsInHand.Length;i++ )
                {
                    if (this.cardsInHand[i] > -1)	//if real card
                    {
                        this.discardPile[++discardCounter] = cardsInHand[i];
                        cardsInHand[i] = -2;		//erase all traces in Hand
                    }
                }
                for (int i = 0; i < this.usedCards.Length; i++)
                {
                    if (this.usedCards[i] > -1)	//if real card
                    {
                        this.discardPile[++discardCounter] = usedCards[i];
                        usedCards[i] = -5;		//erase all traces in Hand
                    }
                }
				this.handCounter=-1;		//reset handCounter
                this.usedCounter = -1;
				
				return true;
            }
            public bool discardDeck()
            {
                return true;
            }
            public bool countPoints()
            {
                return true;
            }
            public bool displayHand()
            {
				Console.Write("\n\nThis is your hand: ");
				for(int i=0;i<this.handCounter+1;i++)
				{
					if(cardsInHand[i] > -1)		//is a real card
					{
						try
						{
							Console.Write("\n{0}.\t{1}",i,allCardTypes[cardsInHand[i]].name);
						}
						catch(NullReferenceException)
						{
							Console.Write("\n\nError using displayHand(): cannot find card in cardTypes for number({0})",cardsInHand[i]);
						}
					}
				}

                //Also display used cards
                Console.Write("\n\nThese are used: ");
                for (int i = 0; i < this.handCounter + 1; i++)
                {
                    if (usedCards[i] > -1)		//is a real card
                    {
                        try
                        {
                            Console.Write("\n{0}.\t{1}", i, allCardTypes[usedCards[i]].name);
                        }
                        catch (NullReferenceException)
                        {
                            Console.Write("\n\nError using displayHand(): cannot find card in cardTypes for number({0})", usedCards[i]);
                        }
                    }
                }
				return true;
            }
			public bool displayStat()
			{
				Console.Write("\n\nActions: {0}\nBuys: {1}\nMoney: {2}", this.actions, this.nbuy, this.money+this.moneyTreasure);
				return true;
			}
			public bool refreshMoneyTreasure()
			{
				this.moneyTreasure = 0;
				for(int i=0;i<this.handCounter+1;i++)
				{
					try
					{
						switch(this.cardsInHand[i])
						{
							case 0:
								this.moneyTreasure ++;	//Copper
								break;
							case 1:
								this.moneyTreasure +=2;	//Silver
								break;
							case 2:
								this.moneyTreasure +=3;	//Gold
								break;
						}
					}
					catch(NullReferenceException)
					{
					}
				}
				return true;
			}
			public bool gainCard(int nType)
			{
				try
				{
					allCardTypes[nType].amount--;  //detta kräver ref (antal i högen...)
				}
				catch(NullReferenceException)
				{
					Console.Write("\nError using gainCard(): do not find card number ({0})!",nType);
				}

                if (allCardTypes[nType].amount == 1)	//only one left and it will be bought now => another empty pile
                {
                    if (nType > 6 && nType < 39) emptyPiles++;		//another action pile becomes empty
                    else if (nType == 5) emptyPiles = 100;			//Provinces become empty!!! (game ends)
                    //now continue and pay
                }

				this.discardPile[++this.discardCounter] = nType;	//add new card at new position in discPile
				this.numOfCardsInTotal++;
				return true;
			}
			public bool buyCard()
			{
				Console.Write("\nWhat card do you want to buy? (write name)\nIf you don't want to buy anything, write: Don't\n");
				string cardName = Console.ReadLine();
                if (cardName == "Don't")
                {
                    this.nbuy = -1;
                    return false;
                }

				int nType = findCard(cardName);
				if (nType==-1)	//ie findCard() did not find type
				{
					Console.Write("\nDo not recognize card name");
					return false;
				}
				if (allCardTypes[nType].cost>this.money) 
				{
					Console.Write("\nNot enough money");
					return false;
				}
				if (allCardTypes[nType].amount<1)
				{
					Console.Write("\nNo cards left of that type");
					return false;
				}
				
				
				this.money-=allCardTypes[nType].cost;	//pay
				this.gainCard(nType);	//gain;	amount of cards decreases inside gainCard()
				return true;
			}
			public bool gainCardToHand(int nType)     //only Mine uses this method so far
			{
				try
				{
					allCardTypes[nType].amount--;  //don't need emptyPileCheck
				}
				catch(NullReferenceException)
				{
					Console.Write("\nError using gainCard(): do not find card!");
				}
				this.cardsInHand[++this.handCounter] = nType;	//add new card at new position in Hand
				this.numOfCardsInTotal++;
				return true;
			}
			public bool trashCardFromHand(int numInHand)	//destroy a card
			{
				
				if(this.cardsInHand[numInHand]== -2)
				{
					Console.Write("\nYou must trash a card. That is not a card, don't try to fool me");
					return false;
				}
                else if (this.cardsInHand[numInHand] >-1)
                {
                    this.cardsInHand[numInHand] = -2;   //trashes card forever
                }
				this.numOfCardsInTotal--;
				return true;
			}
			public bool makeCardUsed(int numberInHand)
			{
                Console.Write("\n\nI have used a card, where is it now?");
                this.usedCards[++usedCounter] = this.cardsInHand[numberInHand];
                this.cardsInHand[numberInHand]= -2;
				return true;
			}
			public bool readUseCard()
			{
				Console.Write("\n\nUse a card from your hand\nwrite number (write a negative number if don't want to use a card): ");
                int command;
                try
                {
                    command = Convert.ToInt32(Console.ReadLine());
                }
                catch (FormatException)
                {
                    Console.Write("\nFool, you didn't write a number");
                    this.actions++;
                    return false;
                }
                //Console.Write("\nYou just entered this number: {0}", command);
                if (command < 0)
                {
                    return true;	//do not want to use an action
                }
                if (this.cardsInHand[command] < 0)  //not a real card
                {
                    cardMethod(-1);	//turns to default, ie no card used
                    return false;   //user wants to use an action but did a mistake
                }
				try
				{
					cardMethod(this.cardsInHand[command]);      //user uses this card
                    makeCardUsed(command);    //puts card on table
				}
				catch (NullReferenceException)
				{
					cardMethod(-1);	//turns to default, ie no card used
				}
				return false;	//wants to use an action
			}
			public bool cardMethod(int CTindex)	//methods for actions
            {
                int numInHand;
                string name;
                switch (CTindex)
                {
					case 7:		//Village
						this.actions +=2;
						this.drawCards(1);
						break;
					case 8:		//Smithy
						this.drawCards(3);
						break;
					case 9:		//Woodcutter
						this.money += 2;
						this.nbuy += 1;
						break;
					case 12:	//Mine
						Console.Write("\nTrash a card from your hand. Which one? (number in hand): ");
				        numInHand = Convert.ToInt32(Console.ReadLine());
                        if (this.cardsInHand[numInHand] < 2 && this.cardsInHand[numInHand] > -1)        //if treasure and not gold
                        {
                            this.trashCardFromHand(numInHand);
                            this.gainCardToHand(numInHand + 1);     //cop -> silv; silv -> gold
                        }
                        else
                        {
                            Console.Write("That was no treasure card, the mine didn't work this time. \nTry another time with a treasure card instead.");
                        }
						break;
                    case 13:    //Moneylender
                        numInHand=this.findCardInHand(0); //find a copper in hand
                        if (numInHand == -1) //no copper was found
                        {
                            Console.Write("\nNo Copper was found in your hand...");
                            break;
                        }
                        this.trashCardFromHand(numInHand);
                        this.money += 3;
                        Console.Write("\nYou have successully trashed a copper for money");
                        break;
					case 14:	//Market
						this.drawCards(1);
						this.actions += 1;
						this.nbuy += 1;
						this.money += 1;
						break;
                    case 16:    //Workshop
                        Console.Write("\n\nGain a card costing up to 4 for free: (write name) ");
                        name = Console.ReadLine();
                        while( allCardTypes[findCard(name)].cost>4)
                        {
                            Console.Write("\nToo expensive, max cost is 4\n\nTry again: (write name) ");
                            name = Console.ReadLine();
                        }
                        this.gainCard(findCard(name));
                        break;
                    case 19:    //Witch
                        this.drawCards(2);
                        attack(19);
                        Console.Write("\nAll other players gain a CURSE card...");
                        break;
					case 21:	//Festival
						this.actions +=2;
						this.nbuy++;
						this.money+=2;
						break;
					case 22:	//Laboratory
						this.drawCards(2);
						this.actions++;
						break;
                    case 25:    //Chapel
                        Console.Write("\nYou may trash up to 4 cards from you hand");
                        int i = 0;
                        Console.Write("\n\nWhat card do you want to trash? (write name)\nWrite Don't to not trash\n");
                        name = Console.ReadLine();
                        while (name!="Don't" && i<4)
                        {
                            numInHand = findCardInHand(findCard(name));
                            while(numInHand<0)
                            {
                                Console.Write("\nDidn't find card, try again (write name) ");
                                name = Console.ReadLine();
                                if (name == "Don't")
                                {
                                    numInHand = 1;
                                }
                                else
                                {
                                    numInHand = findCardInHand(findCard(name));
                                }
                            }

                            this.trashCardFromHand(numInHand);
                            Console.Write("\nYou successfully trashed a {0}",allCardTypes[findCard(name)].name);
                            Console.Write("\n\nWhat card do you want to trash? (write name)\nWrite Don't to not trash\n");
                            name = Console.ReadLine();
                            i++;
                        }
                        break;
                    case 26:    //CouncilRoom
                        this.drawCards(4);
                        this.nbuy++;
                        attack(26);     //All other draw a card.
                        Console.Write("\nAll other players draws a card");
                        break;
                    case 28:    //Militia
                        money += 2;
                        attack(28);     //All others drop down to 3 cards in hand, they choose which to discard
                        break;
                    default:
						Console.Write("\nThat is not an action card, try again.");
						this.actions++;
                        break;
                }
                return true;
            }
        }
        static class shuffle
		{
			static Random rand = new Random();
			
			static int CompareKey(KeyValuePair<int,int> a, KeyValuePair<int, int> b)
			{
				return a.Key.CompareTo(b.Key);
			}
			public static bool shuffleIntArr(ref int[] discardPile, ref int discardCounter, ref int[] deckArr, ref int deckCounter)
			{
				if (deckCounter>-1)	//deckCounter==-1 => empty deck, deckCounter == 0 => there is one card in deck
				{
					Console.Write("\n\nError using shuffle: deck is not empty!");
					return false;
				}
				
				List<KeyValuePair<int, int>> shufflingList = new List<KeyValuePair<int,int>>();
				for(int i=0;i<discardCounter+1;i++)	//i blir nu som högst discardCounter, dvs den översta adressen
				{
					shufflingList.Add(new KeyValuePair<int, int> (rand.Next(), discardPile[i]));
					discardPile[i] = -4; //behövs inte raderas, de kommer skrivas över och Counter tar hand om att bara riktiga kort pekas på (when working: delete this line)
				}
				shufflingList.Sort(CompareKey);	//Sorterar rand int => slumpad ordning
				
				int ind=0;
				foreach(var pair in shufflingList)
				{
					deckArr[ind]=pair.Value;
					ind++;
				}
				deckCounter = discardCounter;
				discardCounter =-1;
				
				return true;
			}
		}
		static int findCard(string cardName)
		{
            for (int i = 0; i < allCardTypes.Length; i++)
			{
                if (allCardTypes[i].name == cardName) return i;
			}
			Console.Write("\nError using findCard(): card not found");
			return -1;	//ie card not found
		}
        static bool initCardTypes()
        {
            allCardTypes[0].assignCard("treasure",  0,  "Copper",	0,	60);
            allCardTypes[1].assignCard("treasure",  1,  "Silver",	3,	40);
            allCardTypes[2].assignCard("treasure",  2,  "Gold",		6,	30);
            allCardTypes[3].assignCard("victory",   3,  "Estate",	2,	24);
            allCardTypes[4].assignCard("victory", 	4,  "Duchy",	5,	12);
            allCardTypes[5].assignCard("victory", 	5,  "Province",	8,	12);
            allCardTypes[6].assignCard("victory", 	6,  "Gardens",	4,	12);
            allCardTypes[7].assignCard("action", 	7,  "Village",	3,	10);
            allCardTypes[8].assignCard("action", 	8,  "Smithy",	4,	10);
            allCardTypes[9].assignCard("action", 	9,  "Woodcutter",3,	10);
			allCardTypes[10].assignCard("action", 	10, "Cellar",	2,	10);
			allCardTypes[11].assignCard("action",	11, "Bureaucrat",4,	10);
			allCardTypes[12].assignCard("action",	12, "Mine",		5,	10);
			allCardTypes[13].assignCard("action",	13, "Moneylender",4,10);
			allCardTypes[14].assignCard("action",	14, "Market",	5,	10);
			allCardTypes[15].assignCard("action",	15, "Remodel",	4,	10);
			allCardTypes[16].assignCard("action",	16, "Workshop",	3,	10);
			allCardTypes[17].assignCard("action",	17, "Spy",		4,	10);
			allCardTypes[18].assignCard("action",	18, "ThroneRoom",4,	10);
			allCardTypes[19].assignCard("action",	19, "Witch",	5,	10);
			allCardTypes[20].assignCard("action",	20, "Library",	5,	10);
			allCardTypes[21].assignCard("action",	21, "Festival",	5,	10);
			allCardTypes[22].assignCard("action",	22, "Laboratory",5,	10);
			allCardTypes[23].assignCard("action",	23, "Adventurer",5,	10);
			allCardTypes[24].assignCard("action",		"Chancellor",3,	10);		//anv här en overloaded function
			allCardTypes[25].assignCard("action",		"Chapel",	2,	10);
			allCardTypes[26].assignCard("action",		"CouncilRoom",5,10);
			allCardTypes[27].assignCard("action",		"Feast",	4,	10);
			allCardTypes[28].assignCard("action",		"Militia",	4,	10);
			//allCardTypes[29].assignCard("action",		"ThroneRoom",4,	10);        //dublett se nr18
			allCardTypes[30].assignCard("action",		"Thief",	4,	10);
			allCardTypes[31].assignCard("action",		"Moat",		2,	10);
			
			allCardTypes[39].assignCard("curse",	39, "Curse",	0,	30);		//alla kort från Dominion finns nu här, de flesta oprogrammerade än så länge,
																			//kan lägga till Intrigue vid intresse
            return true;
        }
        
        
    }
}
